home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS02.ADF / IFF / iffr.c < prev    next >
Text File  |  1989-05-30  |  10KB  |  298 lines

  1. /*----------------------------------------------------------------------*
  2.  * IFFR.C  Support routines for reading IFF-85 files.          11/15/85
  3.  * (IFF is Interchange Format File.)
  4.  *
  5.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  6.  * This software is in the public domain.
  7.  *
  8.  * This version for the Commodore-Amiga computer.
  9.  *----------------------------------------------------------------------*/
  10. #include "iff/iff.h"
  11.  
  12. /* ---------- Read -----------------------------------------------------*/
  13.  
  14. /* ---------- OpenRIFF --------------------------------------------------*/
  15. IFFP OpenRIFF(file0, new0, clientFrame)
  16.     BPTR file0;   GroupContext *new0;  ClientFrame *clientFrame; {
  17.     register BPTR file = file0;
  18.     register GroupContext *new = new0;
  19.     IFFP iffp = IFF_OKAY;
  20.  
  21.     new->parent       = NULL;        /* "whole file" has no parent.*/
  22.     new->clientFrame  = clientFrame;
  23.     new->file         = file;
  24.     new->position     = 0;
  25.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  26.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  27.  
  28.     /* Set new->bound. AmigaDOS specific code.*/
  29.     if (file <= 0)   return(NO_FILE);
  30.     Seek(file, 0, OFFSET_END);            /* Seek to end of file.*/
  31.     new->bound = Seek(file, 0, OFFSET_CURRENT);    /* Pos'n == #bytes in file.*/
  32.     if (new->bound < 0)   return(DOS_ERROR);    /* DOS being absurd.*/
  33.     Seek(file, 0, OFFSET_BEGINNING);        /* Go to file start.*/
  34.     /* Would just do this if Amiga DOS maintained fh_End: */
  35.     /* new->bound = (FileHandle *)BADDR(file)->fh_End; */
  36.  
  37.     if ( new->bound < sizeof(ChunkHeader) )
  38.     iffp = NOT_IFF;
  39.     return(iffp);
  40.     }
  41.  
  42. /* ---------- OpenRGroup -----------------------------------------------*/
  43. IFFP OpenRGroup(parent0, new0)   GroupContext *parent0, *new0; {
  44.     register GroupContext *parent = parent0;
  45.     register GroupContext *new    = new0;
  46.     IFFP iffp = IFF_OKAY;
  47.  
  48.     new->parent       = parent;
  49.     new->clientFrame  = parent->clientFrame;
  50.     new->file         = parent->file;
  51.     new->position     = parent->position;
  52.     new->bound        = parent->position + ChunkMoreBytes(parent);
  53.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  54.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  55.  
  56.     if ( new->bound > parent->bound  ||  IS_ODD(new->bound) )
  57.     iffp = BAD_IFF;
  58.     return(iffp);
  59.     }
  60.  
  61. /* ---------- CloseRGroup -----------------------------------------------*/
  62. IFFP CloseRGroup(context)   GroupContext *context; {
  63.     register LONG position;
  64.  
  65.     if (context->parent == NULL) {
  66.     }  /* Context for whole file.*/
  67.     else {
  68.     position = context->position;
  69.     context->parent->bytesSoFar += position - context->parent->position;
  70.     context->parent->position = position;
  71.     }
  72.     return(IFF_OKAY);
  73.     }
  74.  
  75. /* ---------- SkipFwd --------------------------------------------------*/
  76. /* Skip over bytes in a context. Won't go backwards.*/
  77. /* Updates context->position but not context->bytesSoFar.*/
  78. /* This implementation is AmigaDOS specific.*/
  79. IFFP SkipFwd(context, bytes)   GroupContext *context;  LONG bytes; {
  80.     IFFP iffp = IFF_OKAY;
  81.  
  82.     if (bytes > 0) {
  83.     if (-1 == Seek(context->file, bytes, OFFSET_CURRENT))
  84.         iffp = BAD_IFF;    /* Ran out of bytes before chunk complete.*/
  85.     else
  86.         context->position += bytes;
  87.     }
  88.     return(iffp);
  89.     }
  90.  
  91. /* ---------- GetChunkHdr ----------------------------------------------*/
  92. ID GetChunkHdr(context0)   GroupContext *context0;  {
  93.     register GroupContext *context = context0;
  94.     register IFFP iffp;
  95.     LONG remaining;
  96.  
  97.     /* Skip remainder of previous chunk & padding. */
  98.     iffp = SkipFwd(context,
  99.     ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
  100.     CheckIFFP();
  101.  
  102.     /* Set up to read the new header. */
  103.     context->ckHdr.ckID = BAD_IFF;    /* Until we know it's okay, mark it BAD.*/
  104.     context->subtype    = NULL_CHUNK;
  105.     context->bytesSoFar = 0;
  106.  
  107.     /* Generate a psuedo-chunk if at end-of-context. */
  108.     remaining = context->bound - context->position;
  109.     if (remaining == 0) {
  110.     context->ckHdr.ckSize = 0;
  111.     context->ckHdr.ckID   = END_MARK;
  112.     }
  113.  
  114.     /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
  115.     else if (sizeof(ChunkHeader) > remaining) {
  116.     context->ckHdr.ckSize = remaining;
  117.     }
  118.  
  119.     /* Read the chunk header (finally). */
  120.     else {
  121.         switch (Read(context->file, &context->ckHdr, sizeof(ChunkHeader))) {
  122.         case -1: return(context->ckHdr.ckID = DOS_ERROR);
  123.         case 0:  return(context->ckHdr.ckID = BAD_IFF);
  124.         }
  125.  
  126.     /* Check: Top level chunk must be LIST or FORM or CAT. */
  127.     if (context->parent == NULL)
  128.         switch(context->ckHdr.ckID) {
  129.         case FORM:  case LIST:  case CAT:  break;
  130.         default:    return(context->ckHdr.ckID = NOT_IFF);
  131.         }
  132.  
  133.     /* Update the context. */
  134.     context->position += sizeof(ChunkHeader);
  135.     remaining         -= sizeof(ChunkHeader);
  136.  
  137.     /* Non-positive ID values are illegal and used for error codes.*/
  138.     /* We could check for other illegal IDs...*/
  139.     if (context->ckHdr.ckID <= 0)
  140.          context->ckHdr.ckID = BAD_IFF;
  141.  
  142.     /* Check: ckSize negative or larger than # bytes left in context? */
  143.     else if (context->ckHdr.ckSize < 0  ||
  144.          context->ckHdr.ckSize > remaining) {
  145.         context->ckHdr.ckSize = remaining;
  146.         context->ckHdr.ckID   = BAD_IFF;
  147.         }
  148.  
  149.     /* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
  150.     else switch (context->ckHdr.ckID) {
  151.         case LIST:  case FORM:  case PROP:  case CAT:  {
  152.         iffp = IFFReadBytes(context,
  153.                     (BYTE *)&context->subtype,
  154.                     sizeof(ID));
  155.         if (iffp != IFF_OKAY)
  156.             context->ckHdr.ckID = iffp;
  157.         break; }
  158.         }
  159.  
  160.     }
  161.     return(context->ckHdr.ckID);
  162.     }
  163.  
  164. /* ---------- IFFReadBytes ---------------------------------------------*/
  165. IFFP IFFReadBytes(context, buffer, nBytes)
  166.     GroupContext *context;   BYTE *buffer;   LONG nBytes; {
  167.     register IFFP iffp = IFF_OKAY;
  168.  
  169.     if (nBytes < 0)
  170.     iffp = CLIENT_ERROR;
  171.     else if (nBytes > ChunkMoreBytes(context))
  172.     iffp = SHORT_CHUNK;
  173.     else if (nBytes > 0)
  174.     switch ( Read(context->file, buffer, nBytes) ) {
  175.         case -1: {iffp = DOS_ERROR; break; }
  176.         case 0:  {iffp = BAD_IFF;   break; }
  177.         default: {
  178.         context->position   += nBytes;
  179.         context->bytesSoFar += nBytes;
  180.         }
  181.         }
  182.  
  183.     return(iffp);
  184.     }
  185.  
  186. /* ---------- SkipGroup ------------------------------------------------*/
  187. IFFP SkipGroup(context)  GroupContext *context;  {
  188.     }    /* Nothing to do, thanks to GetChunkHdr */
  189.  
  190. /* ---------- ReadIFF --------------------------------------------------*/
  191. IFFP ReadIFF(file, clientFrame)  BPTR file;  ClientFrame *clientFrame;  {
  192.     /*CompilerBug register*/ IFFP iffp;
  193.     GroupContext context;
  194.  
  195.     iffp = OpenRIFF(file, &context);
  196.     context.clientFrame = clientFrame;
  197.  
  198.     if (iffp == IFF_OKAY)
  199.     switch (iffp = GetChunkHdr(&context)) {
  200.         case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
  201.         case LIST: { iffp = (*clientFrame->getList)(&context); break; }
  202.         case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
  203.         /* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
  204.         }
  205.  
  206.     CloseRGroup(&context);
  207.  
  208.     if (iffp > 0)        /* Make sure we don't return an ID.*/
  209.     iffp = NOT_IFF;        /* GetChunkHdr should've caught this.*/
  210.     return(iffp);
  211.     }
  212.  
  213. /* ---------- ReadIList ------------------------------------------------*/
  214. IFFP ReadIList(parent, clientFrame)
  215.     GroupContext *parent;  ClientFrame *clientFrame; {
  216.     GroupContext listContext;
  217.     IFFP iffp;
  218.     BOOL propOk = TRUE;
  219.  
  220.     iffp = OpenRGroup(parent, &listContext);
  221.     CheckIFFP();
  222.  
  223.     /* One special case test lets us handle CATs as well as LISTs.*/
  224.     if (parent->ckHdr.ckID == CAT)
  225.     propOk = FALSE;
  226.     else
  227.     listContext.clientFrame = clientFrame;
  228.  
  229.     do {
  230.     switch (iffp = GetChunkHdr(&listContext)) {
  231.         case PROP: {
  232.         if (propOk)
  233.             iffp = (*clientFrame->getProp)(&listContext);
  234.         else
  235.             iffp = BAD_IFF;
  236.         break;
  237.         }
  238.         case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
  239.         case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
  240.         case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
  241.         /* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
  242.         }
  243.     if (listContext.ckHdr.ckID != PROP)
  244.         propOk = FALSE;    /* No PROPs allowed after this point.*/
  245.     } while (iffp == IFF_OKAY);
  246.  
  247.     CloseRGroup(&listContext);
  248.  
  249.     if (iffp > 0)    /* Only chunk types above are allowed in a LIST/CAT.*/
  250.     iffp = BAD_IFF;
  251.     return(iffp == END_MARK ? IFF_OKAY : iffp);
  252.     }
  253.  
  254. /* ---------- ReadICat -------------------------------------------------*/
  255. /* By special arrangement with the ReadIList implement'n, this is trivial.*/
  256. IFFP ReadICat(parent)  GroupContext *parent;  {
  257.     return( ReadIList(parent, NULL) );
  258.     }
  259.  
  260. /* ---------- GetFChunkHdr ---------------------------------------------*/
  261. ID GetFChunkHdr(context)   GroupContext *context; {
  262.     register ID id;
  263.  
  264.     id = GetChunkHdr(context);
  265.     if (id == PROP)
  266.     context->ckHdr.ckID = id = BAD_IFF;
  267.     return(id);
  268.     }
  269.  
  270. /* ---------- GetF1ChunkHdr ---------------------------------------------*/
  271. ID GetF1ChunkHdr(context)   GroupContext *context; {
  272.     register ID id;
  273.     register ClientFrame *clientFrame = context->clientFrame;
  274.  
  275.     switch (id = GetChunkHdr(context))  {
  276.     case PROP: { id = BAD_IFF; break; }
  277.     case FORM: { id = (*clientFrame->getForm)(context); break; }
  278.     case LIST: { id = (*clientFrame->getList)(context); break; }
  279.     case CAT : { id = (*clientFrame->getCat )(context); break; }
  280.     /* Default: let the caller handle other chunks */
  281.     }
  282.     return(context->ckHdr.ckID = id);
  283.     }
  284.  
  285. /* ---------- GetPChunkHdr ---------------------------------------------*/
  286. ID GetPChunkHdr(context)   GroupContext *context; {
  287.     register ID id;
  288.  
  289.     id = GetChunkHdr(context);
  290.     switch (id) {
  291.     case LIST:  case FORM:  case PROP:  case CAT:  {
  292.         id = context->ckHdr.ckID = BAD_IFF;
  293.         break; }
  294.     }
  295.     return(id);
  296.     }
  297.  
  298.